home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
src
/
haeberli
/
mtex
/
resample.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
15KB
|
400 lines
/*
* Copyright 1991, 1992, 1993, 1994, Silicon Graphics, Inc.
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
* the contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of Silicon Graphics, Inc.
*
* RESTRICTED RIGHTS LEGEND:
* Use, duplication or disclosure by the Government is subject to restrictions
* as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
* and Computer Software clause at DFARS 252.227-7013, and/or in similar or
* successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
* rights reserved under the Copyright Laws of the United States.
*/
#include <gl/gl.h>
#include <time.h>
#include <math.h>
#include "types.h"
#include "image.h"
#include "colors.h"
#include "other.h"
#define EPS .001
Boolean d;
typedef unsigned int unsi;
extern IMAGE *iopen(char *,char *,unsi type,unsi dim, unsi xsize, unsi ysize, unsi zsize);
IMAGE *oimage;
kernel_list_t kernel; /* Global...created by generate_kernels, used by sample_gaussian */
/***************************************************************************/
void generate_kernels(float st_dev, float threshold)
{
long i,k,region,sgrid,rsize,osize,ksize,ssize,kx,ky,x,y,coeff_count;
float sx,sy,dx,dy,dist,dist_scaled,dev_sq,gauss,sum,inside_sum,scaled_threshold;
float peak_gauss;
float *kernel_tmp,*kc;
long *ks;
short *ko;
Boolean exclusion_ok;
/* Make an array of convolution kernel coefficients. Array will have many */
/* kernels, one for each of a subpixel grid. Both the number of coefficients */
/* and the size of the subpixel grid are dynamic based on the standard */
/* deviation, which is presumably the zoom factor. Each kernel will have a */
/* variable # of coefficients...whatever nearby pixels have coefficients over */
/* threshold will be included. */
if (st_dev < .4) st_dev = .4; /* Don't undersample if magnifying */
region = (long)(10.0 * st_dev);
if (region < 7) region = 7;
sgrid = (long)(16.0 / st_dev);
if (sgrid < 1) sgrid = 1;
kernel.subpixel_grid_size = sgrid;
/* Dynamically allocate storage for arrays pointed to by kernel structure */
ssize = (SQR(sgrid) + 1) * sizeof(long);
rsize = SQR(region) * sizeof(long);
ksize = SQR(sgrid) * SQR(region) * sizeof(float);
osize = SQR(sgrid) * SQR(region) * 2 * sizeof(short);
kernel.starts = (long *)malloc(ssize); /* One "start pointer" per subpixel gridpoint, plus extra at end */
kernel_tmp = (float *)malloc(rsize); /* One kernel_tmp entry per nearby pixel */
kernel.offsets = (short *)malloc(osize); /* Two offsets per coefficient */
kernel.coeffs = (float *)malloc(ksize); /* One coeff per nearby pixel per subpixel gridpoint */
ks = kernel.starts;
kc = kernel.coeffs;
ko = kernel.offsets;
if (kernel.coeffs == NULL)
{
printf("Unable to allocate %d bytes for resampling kernels\n",ksize);
exit(1);
}
coeff_count = 0;
for (ky=0; ky<sgrid; ky++) /* Subpixel offsets...each will be kernel */
for (kx=0; kx<sgrid; kx++)
{
sum = 0.0;
peak_gauss = 0.0;
k = 0;
sx = (float)(region/2) + (float)kx / (float)sgrid; /* Subpixel center */
sy = (float)(region/2) + (float)ky / (float)sgrid;
/* Calculate gaussian coefficient of distance from x,y to sx,sy */
for (y=0; y<region; y++) /* Consider nearby pixel centers */
for (x=0; x<region; x++)
{
dx = sx - (float)x; /* X distance from pixel center to subpixel center */
dy = sy - (float)y; /* Y distance from pixel center to subpixel center */
dist = sqrtf(SQR(dx) + SQR(dy)); /* Euclidean distance */
dist_scaled = dist / st_dev;
dev_sq = SQR(dist_scaled);
gauss = exp(-0.5 * dev_sq); /* Gaussian coefficient for this dist */
kernel_tmp[k++] = gauss;
sum += gauss;
peak_gauss = MAX2(gauss,peak_gauss);
}
/* Sum the coefficients which exceed threshold so we can normalize them */
inside_sum = 0.0;
scaled_threshold = threshold * peak_gauss;
if (peak_gauss / sum < scaled_threshold)
scaled_threshold = peak_gauss / sum;
for (k=0; k<SQR(region); k++) /* Nearby pixel centers */
{
if (kernel_tmp[k]/sum >= scaled_threshold)
inside_sum += kernel_tmp[k];
}
/* Normalize coefficients exceeding threshold and fill in kernel arrays */
*ks++ = (long)(kc - kernel.coeffs); /* x,y coeffs start this far down array */
k = 0;
for (y=0; y<region; y++) /* Nearby pixel centers */
for (x=0; x<region; x++)
{
if (kernel_tmp[k]/sum >= scaled_threshold)
{
*ko++ = x - region/2; /* x,y offset from center pixel */
*ko++ = y - region/2;
*kc++ = kernel_tmp[k]/inside_sum; /* Normalized coefficient */
coeff_count++;
}
k++;
}
}
*ks = (long)(kc - kernel.coeffs); /* One last one to mark end of final entry */
printf("%d x %d subpixel grid, %d x %d pixels considered. Average of %5.1f coeff/sample\n",
sgrid,sgrid,region,region,(float)coeff_count/(float)SQR(sgrid));
free (kernel_tmp); /* Done with this */
}
/***************************************************************************/
long sample_gaussian(image_t *src, float px, float py)
{
long i,num_subpixels,list_id,list_offset,next_list_offset,count,src_pixel;
long integer_x,subpixel_grid_offset_x,src_x,maxx;
long integer_y,subpixel_grid_offset_y,src_y,maxy;
float frac_x,frac_y,red_sum,grn_sum,blu_sum,red,grn,blu,coeff;
float *coeff_ptr;
short *offset_ptr;
maxx = src->whole_xsize-1;
maxy = src->whole_ysize-1;
if (px < 0) px = 0.0;
if (px > maxx) px = (float)maxx;
if (py < 0) py = 0.0;
if (py > maxy) py = (float)maxy;
integer_x = (long)px;
integer_y = (long)py;
frac_x = px - (float)integer_x; /* Between 0 and .9999999 */
frac_y = py - (float)integer_y; /* Between 0 and .9999999 */
num_subpixels = kernel.subpixel_grid_size;
subpixel_grid_offset_x = (long)(frac_x * (float)num_subpixels);
subpixel_grid_offset_y = (long)(frac_y * (float)num_subpixels);
list_id = subpixel_grid_offset_x + subpixel_grid_offset_y * num_subpixels;
list_offset = *(kernel.starts + list_id);
next_list_offset = *(kernel.starts + list_id+1);
count = next_list_offset - list_offset; /* This many coefficients */
coeff_ptr = kernel.coeffs + list_offset;
offset_ptr = kernel.offsets + 2 * list_offset;
red_sum = 0.0;
grn_sum = 0.0;
blu_sum = 0.0;
for (i=0; i<count; i++)
{
src_x = integer_x + (long)*offset_ptr++;
src_y = integer_y + (long)*offset_ptr++;
if (src_x < 0) src_x = 0;
if (src_x > maxx) src_x = maxx;
if (src_y < 0) src_y = 0;
if (src_y > maxy) src_y = maxy;
coeff = *coeff_ptr++;
src_pixel = *IMG_INDEX(src,src_x,src_y);
red = (float)EXTRACT_RED(src_pixel);
grn = (float)EXTRACT_GRN(src_pixel);
blu = (float)EXTRACT_BLU(src_pixel);
red_sum += red * coeff;
grn_sum += grn * coeff;
blu_sum += blu * coeff;
}
return(MAKE_LARGE_RGB((long)red_sum,(long)grn_sum,(long)blu_sum));
}
/***************************************************************************/
/* Copy image with arbitrary warp and/or zoom. Use filter when sampling */
/* source image. Traverse qp quad linearly to address source image. */
/* Traverse allocated memory to address dest image. Dest image will be */
/* dest_width x dest_height pixels. Outer 2-pixel border will be garbage, */
/* "Undo_image" will be source, "Improved_image" destination. */
void *resample_quad(quad_points *qp, long dest_width, long dest_height)
{
long i,j,k,x,y,larger_dim,first_row,last_row,first_col,last_col,num_rows,num_cols;
long *src_ptr,*dest_ptr,*dbg;
long dest_border_x,dest_border_y;
float fwb,fhb,xa,xb,xc,xd,xe,xf,xg,xh,ya,yb,yc,yd,ye,yf,yg,yh;
float fw,xl,xr,xi,ldx,rdx,dx;
float fh,yl,yr,yi,ldy,rdy,dy;
float xzoom,yzoom,zoom,qp_rescale,minx,miny,maxx,maxy;
int d;
if (no_crop)
{
dest_border_x = 0;
dest_border_y = 0;
if ((dest_width != improved_image->whole_xsize) || (dest_height != improved_image->whole_ysize))
{
free_image(improved_image);
improved_image = make_new_image(dest_width,dest_height);
}
}
else
{
dest_border_x = dest_width / 14;
dest_border_y = dest_height / 14;
if ((dest_width != improved_image->interior_xsize) || (dest_height != improved_image->interior_ysize))
{
free_image(improved_image);
improved_image = make_new_image(dest_width + 2 * dest_border_x,dest_height + 2 * dest_border_y);
}
}
/* qp describes source quadrilateral in screen coordinates. The source */
/* image (undo_image) has arbitrary size, but it was resampled so largest */
/* dimension was 512 when it was displayed (i.e., rescaled by "zoom"). */
/* This rescaled image retains the original aspect ratio. To get qp to */
/* align with the source image, we must subtract the window image origin */
/* and scale it so 0-511 on screen is 0-largest_dimension in source image.*/
larger_dim = MAX2(undo_image->interior_xsize,undo_image->interior_ysize);
qp_rescale = (float)larger_dim / 512.;
minx = miny = 999;
maxx = maxy = -999;
for (i=0; i<8; i++)
{
(*qp)[i] -= IMAGE_BORDER;
(*qp)[i] *= qp_rescale;
(*qp)[i] -= 0.5; /* Point-sampled image has integer pixels */
if (EVEN(i))
{
(*qp)[i] += undo_image->x_border;
minx = MIN2(minx,(*qp)[i]);
maxx = MAX2(maxx,(*qp)[i]);
}
else
{
(*qp)[i] += undo_image->y_border;
miny = MIN2(miny,(*qp)[i]);
maxy = MAX2(maxy,(*qp)[i]);
}
}
/* If "crop_1_to_1" is asserted, then no resampling is to be done...just copy */
if (crop_1_to_1)
{
first_row = (long)((*qp)[1] - (float)dest_border_y + 0.5);
last_row = (long)((*qp)[5] + (float)dest_border_y - 0.5);
first_col = (long)((*qp)[0] - (float)dest_border_x + 0.5);
last_col = (long)((*qp)[4] + (float)dest_border_x - 0.5);
src_ptr = undo_image->whole_org + first_col + first_row * undo_image->pixels_per_line;
dest_ptr = improved_image->whole_org;
num_rows = ABS(last_row - first_row + 1);
num_cols = ABS(last_col - first_col + 1);
for (y=first_row; y<=last_row; y++)
{
memcpy(dest_ptr,src_ptr,num_cols * sizeof(long),dest_ptr - improved_image->whole_org);
src_ptr += undo_image->pixels_per_line;
dest_ptr += improved_image->pixels_per_line;
percentdone(y - first_row, num_rows);
}
return;
}
/* qp subscripts: 6,7---4,5 */
/* / | \ */
/* / | \ */
/* 0,1---------2,3 */
/* */
/* */
/* Border labels: d h */
/* */
/* c 67---45 g */
/* / \ */
/* / \ */
/* b 01---------23 f */
/* */
/* a e */
/* qp is source image coordinates selecting quadrilateral which becomes destination interior */
/* a/e/h/d is source image coordinates which become destination whole corner pixels */
/* They are proj_border away from qp, where proj_border is the projection of the destination */
/* border into the source image */
xzoom = (float)dest_width / (maxx - minx);
yzoom = (float)dest_height / (maxy - miny);
zoom = AVG2F(xzoom,yzoom);
generate_kernels(0.4/zoom,0.005); /* Generate lists of coefficients */
fwb = (float)(dest_width - 1 + 2 * dest_border_x); /* Whole image width */
fhb = (float)(dest_height - 1 + 2 * dest_border_y); /* Whole image height */
/* Extrapolate along 01 <--> 23 and 67 <--> 45 to get points b, c and f, g */
/* Assume 1/16th border around rubber band "qp" is the source image border */
if (no_crop)
{
xa = (*qp)[0];
ya = (*qp)[1];
xe = (*qp)[2];
ye = (*qp)[3];
xh = (*qp)[4];
yh = (*qp)[5];
xd = (*qp)[6];
yd = (*qp)[7];
}
else
{
xc = (*qp)[6] - ((*qp)[4] - (*qp)[6]) / 14.0;
yc = (*qp)[7] - ((*qp)[5] - (*qp)[7]) / 14.0;
xb = (*qp)[0] - ((*qp)[2] - (*qp)[0]) / 14.0;
yb = (*qp)[1] - ((*qp)[3] - (*qp)[1]) / 14.0;
xg = (*qp)[4] + ((*qp)[4] - (*qp)[6]) / 14.0;
yg = (*qp)[5] + ((*qp)[5] - (*qp)[7]) / 14.0;
xf = (*qp)[2] + ((*qp)[2] - (*qp)[0]) / 14.0;
yf = (*qp)[3] + ((*qp)[3] - (*qp)[1]) / 14.0;
/* Extrapolate along b <--> c and f <--> g to get points a, d and e, h */
xd = xc + (xc - xb) / 14.0;
yd = yc + (yc - yb) / 14.0;
xa = xb - (xc - xb) / 14.0;
ya = yb - (yc - yb) / 14.0;
xh = xg + (xg - xf) / 14.0;
yh = yg + (yg - yf) / 14.0;
xe = xf - (xg - xf) / 14.0;
ye = yf - (yg - yf) / 14.0;
}
/* We are going to traverse source region in a,e,d,h order */
/* "i" is source starting point, point a */
/* Left side "l" starts there also */
/* Right side "r" starts at point e */
xi = xl = xa;
yi = yl = ya;
xr = xe;
yr = ye;
/* Rate of change along left edge is (d-a)/fhb */
/* Rate of change along right edge is (h-e)/fhb */
ldx = (xd - xa) / fhb;
ldy = (yd - ya) / fhb;
rdx = (xh - xe) / fhb;
rdy = (yh - ye) / fhb;
dest_ptr = improved_image->whole_org; /* Storage for the cropped image */
for (y=0; y<dest_height + 2 * dest_border_y; y++)
{
dx = (xr - xl) / fwb; /* Compute new scanline increments */
dy = (yr - yl) / fwb;
for (x=0; x<dest_width + 2 * dest_border_x; x++)
{ /* Sample & copy */
*dest_ptr++ = sample_gaussian(undo_image,xi,yi);
xi += dx; /* Bump scanline address */
yi += dy;
}
xi = xl += ldx; /* Advance edge walk addresses */
yi = yl += ldy;
xr += rdx;
yr += rdy;
percentdone(y, dest_height + 2 * dest_border_y);
}
free(kernel.starts);
free(kernel.offsets);
free(kernel.coeffs);
}
/********************************************************************************/